/******************************************************************************
 * Copyright (c) 2022 Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *****************************************************************************/
/**	@page PLIC
 *
 *	Introduction
 *	===============
 *   platform-level interrupt controller (PLIC)
 *
 *	API Reference
 *	===============
 *	Header File: plic.h
 */

#ifndef INTERRUPT_H
#define INTERRUPT_H
#include "core.h"

#include "compiler.h"
#include "reg_include/register_b91.h"

typedef struct {
    unsigned char preempt_en;
    unsigned char threshold;
} preempt_config_t;

extern unsigned char g_plic_preempt_en;

typedef enum {
    IRQ0_EXCEPTION,
    IRQ1_SYSTIMER,
    IRQ2_ALG,
    IRQ3_TIMER1,
    IRQ4_TIMER0,
    IRQ5_DMA,
    IRQ6_BMC,
    IRQ7_USB_CTRL_EP_SETUP,
    IRQ8_USB_CTRL_EP_DATA,
    IRQ9_USB_CTRL_EP_STATUS,
    IRQ10_USB_CTRL_EP_SETINF,
    IRQ11_USB_ENDPOINT,
    IRQ12_ZB_DM,
    IRQ13_ZB_BLE,
    IRQ14_ZB_BT,
    IRQ15_ZB_RT,
    IRQ16_PWM,
    IRQ17_PKE,
    IRQ18_UART1,
    IRQ19_UART0,
    IRQ20_DFIFO,
    IRQ21_I2C,
    IRQ22_SPI_AHB,
    IRQ23_SPI_APB,
    IRQ24_USB_PWDN,
    IRQ25_GPIO,
    IRQ26_GPIO2RISC0,
    IRQ27_GPIO2RISC1,
    IRQ28_SOFT,

    IRQ29_NPE_BUS0,
    IRQ30_NPE_BUS1,
    IRQ31_NPE_BUS2,
    IRQ32_NPE_BUS3,
    IRQ33_NPE_BUS4,

    IRQ34_USB_250US,
    IRQ35_USB_RESET,
    IRQ36_NPE_BUS7,
    IRQ37_NPE_BUS8,

    IRQ42_NPE_BUS13 = 42,
    IRQ43_NPE_BUS14,
    IRQ44_NPE_BUS15,

    IRQ46_NPE_BUS17 = 46,

    IRQ50_NPE_BUS21 = 50,
    IRQ51_NPE_BUS22,
    IRQ52_NPE_BUS23,
    IRQ53_NPE_BUS24,
    IRQ54_NPE_BUS25,
    IRQ55_NPE_BUS26,
    IRQ56_NPE_BUS27,
    IRQ57_NPE_BUS28,
    IRQ58_NPE_BUS29,
    IRQ59_NPE_BUS30,
    IRQ60_NPE_BUS31,

    IRQ61_NPE_COMB,
    IRQ62_PM_TM,
    IRQ63_EOC,
} irq_source_e;

typedef enum {
    IRQ_PRI_LEV0,  // Never interrupt
    IRQ_PRI_LEV1,
    IRQ_PRI_LEV2,
    IRQ_PRI_LEV3,
} irq_priority_e;

/**
 * @brief    This function serves to set plic feature.
 * @param[in]   feature - preemptive priority interrupt feature and the vector mode.
 * @return  none
 */
static inline void plic_set_feature(feature_e feature)
{
    reg_irq_feature = feature;  // enable vectored in PLIC
}

/**
 * @brief    This function serves to enable preemptive priority interrupt feature.
 * @return  none
 */
static inline void plic_preempt_feature_en(void)
{
    reg_irq_feature |= FLD_FEATURE_PREEMPT_PRIORITY_INT_EN;
    g_plic_preempt_en = 1;
}

/**
 * @brief    This function serves to enable preemptive priority interrupt feature.
 * @return  none
 */
static inline void plic_preempt_feature_dis(void)
{
    reg_irq_feature &= (~FLD_FEATURE_PREEMPT_PRIORITY_INT_EN);
    g_plic_preempt_en = 0;
}

/**
 * @brief    This function serves to set plic pending.
 * @param[in]  src - interrupt source.
 * @return  none
 */
static inline void plic_set_pending(irq_source_e src)
{
    reg_irq_pending(src) = BIT(src % 32);
}

/**
 * @brief    This function serves to set Priority Threshold,
 *           Only active interrupts with priorities strictly greater than the threshold will cause interrupt.
 * @param[in]   threshold -  threshold level.
 * @return  none
 */
static inline void plic_set_threshold(unsigned char threshold)
{
    reg_irq_threshold = threshold;
}

/**
 * @brief    This function serves to set preemptive priority level,
 *           the priority value 0 is reserved to mean "never interrupt".
 *           The larger the priority value, the higher the interrupt priority.
 * @param[in]   src- interrupt source.
 * @param[in]   priority-  priority level.
 * @return  none
 */
static inline void plic_set_priority(irq_source_e src, irq_priority_e priority)
{
    reg_irq_src_priority(src) = priority;
}

/**
 * @brief    This function serves to enable plic interrupt source.
 * @param[in]   src - interrupt source.
 * @return  none
 */
static inline void plic_interrupt_enable(irq_source_e src)
{
    reg_irq_src(src) |= BIT(src % 32);
}

/**
 * @brief    This function serves to disable plic interrupt source.
 * @param[in]   src - interrupt source.
 * @return  none
 */
static inline void plic_interrupt_disable(irq_source_e src)
{
    reg_irq_src(src) &= (~BIT(src % 32));
}

/**
 * @brief    This function serves to clear interrupt source has completed.
 * @param[in] src - interrupt source.
 * @return  none
 */
static inline void plic_interrupt_complete(irq_source_e src)
{
    reg_irq_done = src;
}

/**
 * @brief    This function serves to claim  interrupt.
 * @return   it return the source id which interrupted in irq_source_e enum .
 */
static inline unsigned int plic_interrupt_claim(void)
{
    return reg_irq_done;
}

/**
 * @brief    This function serves to config plic when enter some function process such as flash.
 * @param[in]   preempt_en - 1 can disturb by interrupt, 0 can't disturb by interrupt.
 * @param[in]   threshold  - interrupt threshold.when the interrupt priority> interrupt threshold,
 *                           the function process will be disturb by interrupt.
 * @return  none
*/
_attribute_ram_code_sec_noinline_ unsigned int plic_enter_critical_sec(unsigned char preempt_en,
                                                                       unsigned char threshold);

/**
 * @brief    This function serves to config plic when exit some function process such as flash.
 * @param[in]   preempt_en - 1 can disturb by interrupt, 0 can disturb by interrupt.
 * @param[in]    r         - the value of mie register to restore.
 * @return  none
*/
_attribute_ram_code_sec_noinline_ void plic_exit_critical_sec(unsigned char preempt_en, unsigned int r);

#endif
